#include <QtCore/QTimer>
#include "smpp/parameters/smpp_prameters_header.h"
#include "smpp/encoders/smpp_binary_encoder.h"
#include "smpp/decoders/smpp_binary_decoder.h"
#include "smpp/sessions/smpp_client_async_session.h"
#include "smpp/sessions/smpp_client_async_session_p.h"

namespace smpp {

namespace sessions {

using namespace pdu;
using namespace decoders;
using namespace encoders;

ClientAsyncSessionData::ClientAsyncSessionData(
  QObject *parent,
  int max_in_speed,
  int max_out_speed,
  int session_init_time,
  int enquire_link_time,
  int inactivity_time,
  int transaction_time,
  int max_in_trans,
  int max_out_trans,
  bool threaded) :
  session_init_time(session_init_time),
  enquire_link_time(enquire_link_time),
  inactivity_time(inactivity_time),
  transaction_time(transaction_time),
  now_in_speed(0),
  now_out_speed(0),
  in_speed_it(0),
  out_speed_it(0),
  session_init_timer(-1),
  enquire_link_timer(-1),
  inactivity_timer(-1),
  main_timer_id(-1),
  error_code(NO_ERROR),
  max_in_speed(max_in_speed),
  max_out_speed(max_out_speed),
  max_in_trans(max_in_trans),
  max_out_trans(max_out_trans),
  now_state(CLOSED),
  socket(new QTcpSocket(parent)),
  peer_port(0),
  local_port(0),
  thread(0) {
  if(QMetaType::type("QHostAddress") == 0)
    qRegisterMetaType<QHostAddress>("QHostAddress");
  if(QMetaType::type("smpp::sessions::StatusMessage") == 0)
    qRegisterMetaType<smpp::sessions::StatusMessage>(
      "smpp::sessions::StatusMessage");
  if(QMetaType::type("smpp::sessions::ErrorCode") == 0)
    qRegisterMetaType<smpp::sessions::ErrorCode>("smpp::sessions::ErrorCode");
  if(QMetaType::type("QAbstractSocket::SocketError") == 0)
    qRegisterMetaType<QAbstractSocket::SocketError>(
      "QAbstractSocket::SocketError");
  if(QMetaType::type("smpp::sessions::SessionStates") == 0)
    qRegisterMetaType<smpp::sessions::SessionStates>(
      "smpp::sessions::SessionStates");
  if(QMetaType::type("smpp::pdu::Header") == 0)
    qRegisterMetaType<smpp::pdu::Header>("smpp::pdu::Header");
  if(QMetaType::type("smpp::pdu::AlertNotification") == 0)
    qRegisterMetaType<smpp::pdu::AlertNotification>(
      "smpp::pdu::AlertNotification");
  if(QMetaType::type("smpp::pdu::BindTransmitter") == 0)
    qRegisterMetaType<smpp::pdu::BindTransmitter>(
      "smpp::pdu::BindTransmitter");
  if(QMetaType::type("smpp::pdu::BindTransmitterResp") == 0)
    qRegisterMetaType<smpp::pdu::BindTransmitterResp>(
      "smpp::pdu::BindTransmitterResp");
  if(QMetaType::type("smpp::pdu::BindReceiver") == 0)
    qRegisterMetaType<smpp::pdu::BindReceiver>("smpp::pdu::BindReceiver");
  if(QMetaType::type("smpp::pdu::BindReceiverResp") == 0)
    qRegisterMetaType<smpp::pdu::BindReceiverResp>(
      "smpp::pdu::BindReceiverResp");
  if(QMetaType::type("smpp::pdu::BindTransceiver") == 0)
    qRegisterMetaType<smpp::pdu::BindTransceiver>(
      "smpp::pdu::BindTransceiver");
  if(QMetaType::type("smpp::pdu::BindTransceiverResp") == 0)
    qRegisterMetaType<smpp::pdu::BindTransceiverResp>(
      "smpp::pdu::BindTransceiverResp");
  if(QMetaType::type("smpp::pdu::Unbind") == 0)
    qRegisterMetaType<smpp::pdu::Unbind>("smpp::pdu::Unbind");
  if(QMetaType::type("smpp::pdu::UnbindResp") == 0)
    qRegisterMetaType<smpp::pdu::UnbindResp>("smpp::pdu::UnbindResp");
  if(QMetaType::type("smpp::pdu::Outbind") == 0)
    qRegisterMetaType<smpp::pdu::Outbind>("smpp::pdu::Outbind");
  if(QMetaType::type("smpp::pdu::GenericNack") == 0)
    qRegisterMetaType<smpp::pdu::GenericNack>("smpp::pdu::GenericNack");
  if(QMetaType::type("smpp::pdu::SubmitSm") == 0)
    qRegisterMetaType<smpp::pdu::SubmitSm>("smpp::pdu::SubmitSm");
  if(QMetaType::type("smpp::pdu::SubmitSmResp") == 0)
    qRegisterMetaType<smpp::pdu::SubmitSmResp>("smpp::pdu::SubmitSmResp");
  if(QMetaType::type("smpp::pdu::SubmitMulti") == 0)
    qRegisterMetaType<smpp::pdu::SubmitMulti>("smpp::pdu::SubmitMulti");
  if(QMetaType::type("smpp::pdu::SubmitMultiResp") == 0)
    qRegisterMetaType<smpp::pdu::SubmitMultiResp>(
      "smpp::pdu::SubmitMultiResp");
  if(QMetaType::type("smpp::pdu::DeliverSm") == 0)
    qRegisterMetaType<smpp::pdu::DeliverSm>("smpp::pdu::DeliverSm");
  if(QMetaType::type("smpp::pdu::DeliverSmResp") == 0)
    qRegisterMetaType<smpp::pdu::DeliverSmResp>("smpp::pdu::DeliverSmResp");
  if(QMetaType::type("smpp::pdu::DataSm") == 0)
    qRegisterMetaType<smpp::pdu::DataSm>("smpp::pdu::DataSm");
  if(QMetaType::type("smpp::pdu::DataSmResp") == 0)
    qRegisterMetaType<smpp::pdu::DataSmResp>("smpp::pdu::DataSmResp");
  if(QMetaType::type("smpp::pdu::QuerySm") == 0)
    qRegisterMetaType<smpp::pdu::QuerySm>("smpp::pdu::QuerySm");
  if(QMetaType::type("smpp::pdu::QuerySmResp") == 0)
    qRegisterMetaType<smpp::pdu::QuerySmResp>("smpp::pdu::QuerySmResp");
  if(QMetaType::type("smpp::pdu::ReplaceSm") == 0)
    qRegisterMetaType<smpp::pdu::ReplaceSm>("smpp::pdu::ReplaceSm");
  if(QMetaType::type("smpp::pdu::ReplaceSmResp") == 0)
    qRegisterMetaType<smpp::pdu::ReplaceSmResp>("smpp::pdu::ReplaceSmResp");
  if(QMetaType::type("smpp::pdu::EnquireLink") == 0)
    qRegisterMetaType<smpp::pdu::EnquireLink>("smpp::pdu::EnquireLink");
  if(QMetaType::type("smpp::pdu::EnquireLinkResp") == 0)
    qRegisterMetaType<smpp::pdu::EnquireLinkResp>(
      "smpp::pdu::EnquireLinkResp");

  if(threaded) {
    thread = new QThread(parent);
    parent->moveToThread(thread);
  }

  if(max_in_speed < 1) {
    this->max_in_speed = DEFAULT_IN_SPEED;
  }
  if(max_out_speed < 1) {
    this->max_out_speed = DEFAULT_OUT_SPEED;
  }
}

ClientAsyncSessionData::~ClientAsyncSessionData() {
  if(socket) {
    socket->deleteLater();
    socket = 0;
  }
  if(thread) {
    delete thread;
    thread = 0;
  }
}

ClientAsyncSessionPrivate::ClientAsyncSessionPrivate(
  ClientAsyncSession *parent,
  int max_in_speed, // msec
  int max_out_speed, // msec
  int session_init_time,
  int enquire_link_time,
  int inactivity_time,
  int transaction_time,
  int max_in_trans,
  int max_out_trans,
  bool threaded) :
  QObject(0),
  d(new ClientAsyncSessionData(this,
                               max_in_speed,
                               max_out_speed,
                               session_init_time,
                               enquire_link_time,
                               inactivity_time,
                               transaction_time,
                               max_in_trans,
                               max_out_trans,
                               threaded)),
  parent(parent) {

  connect(this, SIGNAL(sigOpenSession(const QHostAddress &, quint16)),
          this, SLOT(openSession(const QHostAddress &, quint16)));
  connect(this, SIGNAL(sigCloseSession()),
          this, SLOT(closeSession()));
  connect(this, SIGNAL(sigSend(const smpp::pdu::BindTransmitter &)),
          this, SLOT(send(const smpp::pdu::BindTransmitter &)));
  connect(this, SIGNAL(sigSend(const smpp::pdu::BindReceiver &)),
          this, SLOT(send(const smpp::pdu::BindReceiver &)));
  connect(this, SIGNAL(sigSend(const smpp::pdu::BindTransceiver &)),
          this, SLOT(send(const smpp::pdu::BindTransceiver &)));
  connect(this, SIGNAL(sigSend(const smpp::pdu::Unbind &)),
          this, SLOT(send(const smpp::pdu::Unbind &)));
  connect(this, SIGNAL(sigSend(const smpp::pdu::GenericNack &)),
          this, SLOT(send(const smpp::pdu::GenericNack &)));
  connect(this, SIGNAL(sigSend(const smpp::pdu::SubmitSm &)),
          this, SLOT(send(const smpp::pdu::SubmitSm &)));
  connect(this, SIGNAL(sigSend(const smpp::pdu::SubmitMulti &)),
          this, SLOT(send(const smpp::pdu::SubmitMulti &)));
  connect(this, SIGNAL(sigSend(const smpp::pdu::DeliverSmResp &)),
          this, SLOT(send(const smpp::pdu::DeliverSmResp &)));
  connect(this, SIGNAL(sigSend(const smpp::pdu::DataSm &)),
          this, SLOT(send(const smpp::pdu::DataSm &)));
  connect(this, SIGNAL(sigSend(const smpp::pdu::DataSmResp &)),
          this, SLOT(send(const smpp::pdu::DataSmResp &)));
  connect(this, SIGNAL(sigSend(const smpp::pdu::QuerySm &)),
          this, SLOT(send(const smpp::pdu::QuerySm &)));
  connect(this, SIGNAL(sigSend(const smpp::pdu::ReplaceSm &)),
          this, SLOT(send(const smpp::pdu::ReplaceSm &)));
  connect(this, SIGNAL(sigSend(const smpp::pdu::EnquireLink &)),
          this, SLOT(send(const smpp::pdu::EnquireLink &)));
  connect(this, SIGNAL(sigSend(const smpp::pdu::EnquireLinkResp &)),
          this, SLOT(send(const smpp::pdu::EnquireLinkResp &)));

  connect(d->socket, SIGNAL(error(QAbstractSocket::SocketError)),
          this, SLOT(socketError(QAbstractSocket::SocketError)));
  connect(d->socket, SIGNAL(readyRead()),
          this, SLOT(socketReadyRead()));
}

ClientAsyncSessionPrivate::~ClientAsyncSessionPrivate() {
  clearAllQueue(NOTVALIDSTATE);
  parent = 0;
}

void ClientAsyncSessionPrivate::send(const BindTransmitter &p) {
  if(p.getCommandId() == parameters::command_id::BIND_TRANSMITTER) {
    if(d->now_state == OPEN || d->now_state == OUTBOUND) {
      if(startOutTransaction(p)) {
        BinaryEncoder encoder;
        encoder << p;
        if(writeSocketData(encoder)) {
          emit parent->outStatus(p, SENDED);
        } else {
          deleteOutTransaction(p);
          emit parent->outStatus(p, NOTSENDED);
        }
      } else {
        emit parent->outStatus(p, TRANSFULL);
      }
    } else {
      emit parent->outStatus(p, NOTVALIDSTATE);
    }
  } else {
    emit parent->outStatus(p, ERRORCID);
  }
}

void ClientAsyncSessionPrivate::send(const BindReceiver &p) {
  if(p.getCommandId() == parameters::command_id::BIND_RECEIVER) {
    if(d->now_state == OPEN || d->now_state == OUTBOUND) {
      if(startOutTransaction(p)) {
        BinaryEncoder encoder;
        encoder << p;
        if(writeSocketData(encoder)) {
          emit parent->outStatus(p, SENDED);
        } else {
          deleteOutTransaction(p);
          emit parent->outStatus(p, NOTSENDED);
        }
      } else {
        emit parent->outStatus(p, TRANSFULL);
      }
    } else {
      emit parent->outStatus(p, NOTVALIDSTATE);
    }
  } else {
    emit parent->outStatus(p, ERRORCID);
  }
}

void ClientAsyncSessionPrivate::send(const BindTransceiver &p) {
  if(p.getCommandId() == parameters::command_id::BIND_TRANSCEIVER) {
    if(d->now_state == OPEN || d->now_state == OUTBOUND) {
      if(startOutTransaction(p)) {
        BinaryEncoder encoder;
        encoder << p;
        if(writeSocketData(encoder)) {
          emit parent->outStatus(p, SENDED);
        } else {
          deleteOutTransaction(p);
          emit parent->outStatus(p, NOTSENDED);
        }
      } else {
        emit parent->outStatus(p, TRANSFULL);
      }
    } else {
      emit parent->outStatus(p, NOTVALIDSTATE);
    }
  } else {
    emit parent->outStatus(p, ERRORCID);
  }
}

void ClientAsyncSessionPrivate::send(const Unbind &p) {
  if(p.getCommandId() == parameters::command_id::UNBIND) {
    if(d->now_state == BOUND_TX ||
       d->now_state == BOUND_RX ||
       d->now_state == BOUND_TRX) {
      if(startOutTransaction(p)) {
        BinaryEncoder encoder;
        encoder << p;
        if(writeSocketData(encoder)) {
          if(p.getCommandStatus() == parameters::command_status::ESME_ROK) {
            setState(UNBOUND);
          }
          emit parent->outStatus(p, SENDED);
        } else {
          deleteOutTransaction(p);
          emit parent->outStatus(p, NOTSENDED);
        }
      } else {
        emit parent->outStatus(p, TRANSFULL);
      }
    } else {
      emit parent->outStatus(p, NOTVALIDSTATE);
    }
  } else {
    emit parent->outStatus(p, ERRORCID);
  }
}

void ClientAsyncSessionPrivate::send(const GenericNack &p) {
  if(p.getCommandId() == parameters::command_id::GENERIC_NACK) {
    if(d->now_state != CLOSED) {
      BinaryEncoder encoder;
      encoder << p;
      if(writeSocketData(encoder)) {
        emit parent->outStatus(p, SENDED);
      } else {
        emit parent->outStatus(p, NOTSENDED);
      }
    } else {
      emit parent->outStatus(p, NOTVALIDSTATE);
    }
  } else {
    emit parent->outStatus(p, ERRORCID);
  }
}

void ClientAsyncSessionPrivate::send(const SubmitSm &p) {
  if(p.getCommandId() == parameters::command_id::SUBMIT_SM) {
    if(d->now_state == BOUND_TX || d->now_state == BOUND_TRX) {
      if(checkOutSpeed()) {
        if(startOutTransaction(p)) {
          BinaryEncoder encoder;
          encoder << p;
          if(writeSocketData(encoder)) {
            emit parent->outStatus(p, SENDED);
          } else {
            deleteOutTransaction(p);
            emit parent->outStatus(p, NOTSENDED);
          }
        } else {
          emit parent->outStatus(p, TRANSFULL);
        }
      } else {
        emit parent->outStatus(p, MAXSPEED);
      }
    } else {
      emit parent->outStatus(p, NOTVALIDSTATE);
    }
  } else {
    emit parent->outStatus(p, ERRORCID);
  }
}

void ClientAsyncSessionPrivate::send(const SubmitMulti &p) {
  if(p.getCommandId() == parameters::command_id::SUBMIT_MULTI) {
    if(d->now_state == BOUND_TX || d->now_state == BOUND_TRX) {
      if(checkOutSpeed()) {
        if(startOutTransaction(p)) {
          BinaryEncoder encoder;
          encoder << p;
          if(writeSocketData(encoder)) {
            emit parent->outStatus(p, SENDED);
          } else {
            deleteOutTransaction(p);
            emit parent->outStatus(p, NOTSENDED);
          }
        } else {
          emit parent->outStatus(p, TRANSFULL);
        }
      } else {
        emit parent->outStatus(p, MAXSPEED);
      }
    } else {
      emit parent->outStatus(p, NOTVALIDSTATE);
    }
  } else {
    emit parent->outStatus(p, ERRORCID);
  }
}

void ClientAsyncSessionPrivate::send(const DeliverSmResp &p) {
  if(p.getCommandId() == parameters::command_id::DELIVER_SM_RESP) {
    if(d->now_state == BOUND_RX || d->now_state == BOUND_TRX) {
      if(stopInTransactions(p)) {
        BinaryEncoder encoder;
        encoder << p;
        if(writeSocketData(encoder)) {
          emit parent->outStatus(p, SENDED);
        } else {
          emit parent->outStatus(p, NOTSENDED);
        }
      } else {
        emit parent->outStatus(p, NOTFOUNDTRANS);
      }
    } else {
      emit parent->outStatus(p, NOTVALIDSTATE);
    }
  } else {
    emit parent->outStatus(p, ERRORCID);
  }
}

void ClientAsyncSessionPrivate::send(const DataSm &p) {
  if(p.getCommandId() == parameters::command_id::DATA_SM) {
    if(d->now_state == BOUND_TX || d->now_state == BOUND_TRX) {
      if(checkOutSpeed()) {
        if(startOutTransaction(p)) {
          BinaryEncoder encoder;
          encoder << p;
          if(writeSocketData(encoder)) {
            emit parent->outStatus(p, SENDED);
          } else {
            deleteOutTransaction(p);
            emit parent->outStatus(p, NOTSENDED);
          }
        } else {
          emit parent->outStatus(p, TRANSFULL);
        }
      } else {
        emit parent->outStatus(p, MAXSPEED);
      }
    } else {
      emit parent->outStatus(p, NOTVALIDSTATE);
    }
  } else {
    emit parent->outStatus(p, ERRORCID);
  }
}

void ClientAsyncSessionPrivate::send(const DataSmResp &p) {
  if(p.getCommandId() == parameters::command_id::DATA_SM_RESP) {
    if(d->now_state == BOUND_RX || d->now_state == BOUND_TRX) {
      if(startOutTransaction(p)) {
        BinaryEncoder encoder;
        encoder << p;
        if(writeSocketData(encoder)) {
          emit parent->outStatus(p, SENDED);
        } else {
          emit parent->outStatus(p, NOTSENDED);
        }
      } else {
        emit parent->outStatus(p, NOTFOUNDTRANS);
      }
    } else {
      emit parent->outStatus(p, NOTVALIDSTATE);
    }
  } else {
    emit parent->outStatus(p, ERRORCID);
  }
}

void ClientAsyncSessionPrivate::send(const QuerySm &p) {
  if(p.getCommandId() == parameters::command_id::QUERY_SM) {
    if(d->now_state == BOUND_TX || d->now_state == BOUND_TRX) {
      if(startOutTransaction(p)) {
        BinaryEncoder encoder;
        encoder << p;
        if(writeSocketData(encoder)) {
          emit parent->outStatus(p, SENDED);
        } else {
          deleteOutTransaction(p);
          emit parent->outStatus(p, NOTSENDED);
        }
      } else {
        emit parent->outStatus(p, TRANSFULL);
      }
    } else {
      emit parent->outStatus(p, NOTVALIDSTATE);
    }
  } else {
    emit parent->outStatus(p, ERRORCID);
  }
}

void ClientAsyncSessionPrivate::send(const ReplaceSm &p) {
  if(p.getCommandId() == parameters::command_id::REPLACE_SM) {
    if(d->now_state == BOUND_TX || d->now_state == BOUND_TRX) {
      if(startOutTransaction(p)) {
        BinaryEncoder encoder;
        encoder << p;
        if(writeSocketData(encoder)) {
          emit parent->outStatus(p, SENDED);
        } else {
          deleteOutTransaction(p);
          emit parent->outStatus(p, NOTSENDED);
        }
      } else {
        emit parent->outStatus(p, TRANSFULL);
      }
    } else {
      emit parent->outStatus(p, NOTVALIDSTATE);
    }
  } else {
    emit parent->outStatus(p, ERRORCID);
  }
}

void ClientAsyncSessionPrivate::send(const CancelSm &p) {
  if(p.getCommandId() == parameters::command_id::CANCEL_SM) {
    if(d->now_state == BOUND_TX || d->now_state == BOUND_TRX) {
      if(startOutTransaction(p)) {
        BinaryEncoder encoder;
        encoder << p;
        if(writeSocketData(encoder)) {
          emit parent->outStatus(p, SENDED);
        } else {
          deleteOutTransaction(p);
          emit parent->outStatus(p, NOTSENDED);
        }
      } else {
        emit parent->outStatus(p, TRANSFULL);
      }
    } else {
      emit parent->outStatus(p, NOTVALIDSTATE);
    }
  } else {
    emit parent->outStatus(p, ERRORCID);
  }
}

void ClientAsyncSessionPrivate::send(const EnquireLink &p) {
  if(p.getCommandId() == parameters::command_id::ENQUIRE_LINK) {
    if(d->now_state != CLOSED) {
      if(startOutTransaction(p)) {
        BinaryEncoder encoder;
        encoder << p;
        if(writeSocketData(encoder)) {
          emit parent->outStatus(p, SENDED);
        } else {
          deleteOutTransaction(p);
          emit parent->outStatus(p, NOTSENDED);
        }
      } else {
        emit parent->outStatus(p, TRANSFULL);
      }
    } else {
      emit parent->outStatus(p, NOTVALIDSTATE);
    }
  } else {
    emit parent->outStatus(p, ERRORCID);
  }
}

void ClientAsyncSessionPrivate::send(const EnquireLinkResp &p) {
  if(p.getCommandId() == parameters::command_id::ENQUIRE_LINK_RESP) {
    if(d->now_state != CLOSED) {
      if(stopInTransactions(p)) {
        BinaryEncoder encoder;
        encoder << p;
        if(writeSocketData(encoder)) {
          emit parent->outStatus(p, SENDED);
        } else {
          emit parent->outStatus(p, NOTSENDED);
        }
      } else {
        emit parent->outStatus(p, NOTFOUNDTRANS);
      }
    } else {
      emit parent->outStatus(p, NOTVALIDSTATE);
    }
  } else {
    emit parent->outStatus(p, ERRORCID);
  }
}

void ClientAsyncSessionPrivate::openSession(
  const QHostAddress &host_name, quint16 port) {
  if(d->now_state == CLOSED &&
     d->socket->state() == QAbstractSocket::UnconnectedState) {
    d->peer_address = host_name;
    d->peer_port = port;
    d->socket->connectToHost(host_name, port);
    if(d->socket->waitForConnected(ClientAsyncSessionData::sock_oper_timeout)) {
      d->local_address = d->socket->localAddress();
      d->local_port = d->socket->localPort();
      setState(OPEN);
    } else {
      procError(CONN_SOCK_ERR,
                QString("Timeout connect from %1:%2")
                  .arg(host_name.toString())
                  .arg(port));
    }
  }
}

void ClientAsyncSessionPrivate::closeSession() {
  if(d->socket->state() == QAbstractSocket::ConnectedState) {
    d->socket->disconnectFromHost();
    if(d->socket->state() == QAbstractSocket::UnconnectedState ||
       d->socket->waitForDisconnected(
         ClientAsyncSessionData::sock_oper_timeout)) {
      setState(CLOSED);
    } else {
      procError(DISC_SOCK_ERR,
                QString("Timeout disconnect from %1:%2")
                  .arg(d->peer_address.toString())
                  .arg(d->peer_port));
    }
  } else {
    setState(CLOSED);
  }
}

void ClientAsyncSessionPrivate::socketError(
  QAbstractSocket::SocketError error) {
  if(error == QAbstractSocket::RemoteHostClosedError &&
     (d->now_state == OPEN ||
      d->now_state == UNBOUND ||
      d->now_state == OUTBOUND)) {
    setState(CLOSED);
  } else {
    procError(NET_SOCK_ERR, d->socket->errorString());
  }
}

void ClientAsyncSessionPrivate::socketReadyRead() {
  while(d->socket->bytesAvailable() > 0) {
    QByteArray bytes;
    quint32 read_bytes(0);

    while(read_bytes != Header::HEADER_LENGTH) {
      bytes += d->socket->read(Header::HEADER_LENGTH - read_bytes);

      read_bytes = bytes.count();

      if(read_bytes != Header::HEADER_LENGTH) {
        if(!d->socket->waitForReadyRead(
             ClientAsyncSessionData::sock_oper_timeout)) {
          return procError(RD_SOCK_ERR, "Not read bytes for decode header");
        }
      }
    }

    BinaryDecoder decoder(bytes);

    bytes.clear();
    read_bytes = 0;

    Header header;

    decoder.decode(header);

    emit parent->inStatus(header, RECEIVED);

    if(header.getCommandLength() < Header::HEADER_LENGTH) {
      emit parent->inStatus(header, ERRORDECODE);
      send(GenericNack(parameters::command_status::ESME_RINVCMDLEN,
                       header.getSequenceNumber()));
      return;
    }

    quint32 body_length(header.getCommandLength() - Header::HEADER_LENGTH);

    while(read_bytes != body_length) {
      bytes += d->socket->read(body_length - read_bytes);

      read_bytes = bytes.count();

      if(read_bytes != body_length) {
        if(!d->socket->waitForReadyRead(
          ClientAsyncSessionData::sock_oper_timeout)) {
          return procError(RD_SOCK_ERR, "Not read bytes for decode body");
          return;
        }
      }
    }

    if(read_bytes > 0) {
      decoder.addBody(bytes);
      bytes.clear();
    }

    switch(header.getCommandId()) {
      case parameters::command_id::BIND_TRANSMITTER_RESP: {
        BindTransmitterResp p(header);
        if(decoder.decode(p)) {
          if(d->now_state == OPEN || d->now_state == OUTBOUND) {
            if(stopOutTransactions(p)) {
              if(p.getCommandStatus() == parameters::command_status::ESME_ROK) {
                setState(BOUND_TX);
              }
              emit parent->receive(p);
            } else {
              emit parent->inStatus(header, NOTFOUNDTRANS);
            }
          } else {
            emit parent->inStatus(header, NOTVALIDSTATE);
          }
        } else {
          emit parent->inStatus(header, ERRORDECODE);
          GenericNack gn(parameters::command_status::ESME_RINVCMDLEN,
                         header.getSequenceNumber());
          BinaryEncoder encoder;
          encoder << gn;
          writeSocketData(encoder);
        }
        break;
      }
      case parameters::command_id::BIND_RECEIVER_RESP: {
        BindReceiverResp p(header);
        if(decoder.decode(p)) {
          if(d->now_state == OPEN || d->now_state == OUTBOUND) {
            if(stopOutTransactions(p)) {
              if(p.getCommandStatus() == parameters::command_status::ESME_ROK) {
                setState(BOUND_RX);
              }
              emit parent->receive(p);
            } else {
              emit parent->inStatus(header, NOTFOUNDTRANS);
            }
          } else {
            emit parent->inStatus(header, NOTVALIDSTATE);
          }
        } else {
          emit parent->inStatus(header, ERRORDECODE);
          GenericNack gn(parameters::command_status::ESME_RINVCMDLEN,
                         header.getSequenceNumber());
          BinaryEncoder encoder;
          encoder << gn;
          writeSocketData(encoder);
        }
        break;
      }
      case parameters::command_id::BIND_TRANSCEIVER_RESP: {
        BindTransceiverResp p(header);
        if(decoder.decode(p)) {
          if(d->now_state == OPEN || d->now_state == OUTBOUND) {
            if(stopOutTransactions(p)) {
              if(p.getCommandStatus() == parameters::command_status::ESME_ROK) {
                setState(BOUND_TRX);
              }
              emit parent->receive(p);
            } else {
              emit parent->inStatus(header, NOTFOUNDTRANS);
            }
          } else {
            emit parent->inStatus(header, NOTVALIDSTATE);
          }
        } else {
          emit parent->inStatus(header, ERRORDECODE);
          GenericNack gn(parameters::command_status::ESME_RINVCMDLEN,
                         header.getSequenceNumber());
          BinaryEncoder encoder;
          encoder << gn;
          writeSocketData(encoder);
        }
        break;
      }
      case parameters::command_id::OUTBIND: {
        if(d->now_state == OPEN) {
          if(header.getCommandStatus() ==
             parameters::command_status::ESME_ROK) {
            setState(OUTBOUND);
          }
          emit parent->receive(Outbind(header));
        } else {
          emit parent->inStatus(header, NOTVALIDSTATE);
        }
        break;
      }
      case parameters::command_id::UNBIND_RESP: {
        if(d->now_state == UNBOUND) {
          if(stopOutTransactions(header)) {
            emit parent->receive(UnbindResp(header));
            closeSession();
          } else {
            emit parent->inStatus(header, NOTFOUNDTRANS);
          }
        } else {
          emit parent->inStatus(header, NOTVALIDSTATE);
        }
        break;
      }
      case parameters::command_id::GENERIC_NACK: {
        if(d->now_state != CLOSED) {
          emit parent->receive(GenericNack(header));
        } else {
          emit parent->inStatus(header, NOTVALIDSTATE);
        }
        break;
      }
      case parameters::command_id::SUBMIT_SM_RESP: {
        SubmitSmResp p(header);
        if(decoder.decode(p)) {
          if(d->now_state == BOUND_TX || d->now_state == BOUND_TRX) {
            if(stopOutTransactions(p)) {
              emit parent->receive(p);
            } else {
              emit parent->inStatus(header, NOTFOUNDTRANS);
            }
          } else {
            emit parent->inStatus(header, NOTVALIDSTATE);
          }
        } else {
          emit parent->inStatus(header, ERRORDECODE);
          GenericNack gn(parameters::command_status::ESME_RINVCMDLEN,
                         header.getSequenceNumber());
          BinaryEncoder encoder;
          encoder << gn;
          writeSocketData(encoder);
        }
        break;
      }
      case parameters::command_id::SUBMIT_MULTI_RESP: {
        SubmitMultiResp p(header);
        if(decoder.decode(p)) {
          if(d->now_state == BOUND_TX || d->now_state == BOUND_TRX) {
            if(stopOutTransactions(p)) {
              emit parent->receive(p);
            } else {
              emit parent->inStatus(header, NOTFOUNDTRANS);
            }
          } else {
            emit parent->inStatus(header, NOTVALIDSTATE);
          }
        } else {
          emit parent->inStatus(header, ERRORDECODE);
          GenericNack gn(parameters::command_status::ESME_RINVCMDLEN,
                         header.getSequenceNumber());
          BinaryEncoder encoder;
          encoder << gn;
          writeSocketData(encoder);
        }
        break;
      }
      case parameters::command_id::DELIVER_SM: {
        DeliverSm p(header);
        if(decoder.decode(p)) {
          if(d->now_state == BOUND_RX || d->now_state == BOUND_TRX) {
            if(checkInSpeed()) {
              if(startInTransaction(p)) {
                emit parent->receive(p);
              } else {
                emit parent->inStatus(header, TRANSFULL);
                DeliverSmResp p_resp(parameters::command_status::ESME_RMSGQFUL,
                                     header.getSequenceNumber());
                BinaryEncoder encoder;
                encoder << p_resp;
                writeSocketData(encoder);
              }
            } else {
              emit parent->inStatus(header, MAXSPEED);
              DeliverSmResp p_resp(parameters::command_status::ESME_RMSGQFUL,
                                   header.getSequenceNumber());
              BinaryEncoder encoder;
              encoder << p_resp;
              writeSocketData(encoder);
            }
          } else {
            emit parent->inStatus(header, NOTVALIDSTATE);
            DeliverSmResp p_resp(parameters::command_status::ESME_RINVBNDSTS,
                                 header.getSequenceNumber());
            BinaryEncoder encoder;
            encoder << p_resp;
            writeSocketData(encoder);
          }
        } else {
          emit parent->inStatus(header, ERRORDECODE);
          GenericNack gn(parameters::command_status::ESME_RINVCMDLEN,
                         header.getSequenceNumber());
          BinaryEncoder encoder;
          encoder << gn;
          writeSocketData(encoder);
        }
        break;
      }
      case parameters::command_id::DATA_SM: {
        DataSm p(header);
        if(decoder.decode(p)) {
          if(d->now_state == BOUND_RX || d->now_state == BOUND_TRX) {
            if(checkInSpeed()) {
              if(startInTransaction(p)) {
                emit parent->receive(p);
              } else {
                emit parent->inStatus(header, TRANSFULL);
                DataSmResp p_resp(parameters::command_status::ESME_RMSGQFUL,
                                  header.getSequenceNumber());
                BinaryEncoder encoder;
                encoder << p_resp;
                writeSocketData(encoder);
              }
            } else {
              emit parent->inStatus(header, MAXSPEED);
              DataSmResp p_resp(parameters::command_status::ESME_RMSGQFUL,
                                header.getSequenceNumber());
              BinaryEncoder encoder;
              encoder << p_resp;
              writeSocketData(encoder);
            }
          } else {
            emit parent->inStatus(header, NOTVALIDSTATE);
            DataSmResp p_resp(parameters::command_status::ESME_RINVBNDSTS,
                              header.getSequenceNumber());
            BinaryEncoder encoder;
            encoder << p_resp;
            writeSocketData(encoder);
          }
        } else {
          emit parent->inStatus(header, ERRORDECODE);
          GenericNack gn(parameters::command_status::ESME_RINVCMDLEN,
                         header.getSequenceNumber());
          BinaryEncoder encoder;
          encoder << gn;
          writeSocketData(encoder);
        }
        break;
      }
      case parameters::command_id::DATA_SM_RESP: {
        DataSmResp p(header);
        if(decoder.decode(p)) {
          if(d->now_state == BOUND_TX || d->now_state == BOUND_TRX) {
            if(stopOutTransactions(p)) {
              emit parent->receive(p);
            } else {
              emit parent->inStatus(header, NOTFOUNDTRANS);
            }
          } else {
            emit parent->inStatus(header, NOTVALIDSTATE);
          }
        } else {
          emit parent->inStatus(header, ERRORDECODE);
          send(GenericNack(parameters::command_status::ESME_RINVCMDLEN,
                           header.getSequenceNumber()));
        }
        break;
      }
      case parameters::command_id::QUERY_SM_RESP: {
        QuerySmResp p(header);
        if(decoder.decode(p)) {
          if(d->now_state == BOUND_TX || d->now_state == BOUND_TRX) {
            if(stopOutTransactions(p)) {
              emit parent->receive(p);
            } else {
              emit parent->inStatus(header, NOTFOUNDTRANS);
            }
          } else {
            emit parent->inStatus(header, NOTVALIDSTATE);
          }
        } else {
          emit parent->inStatus(header, ERRORDECODE);
          send(GenericNack(parameters::command_status::ESME_RINVCMDLEN,
                           header.getSequenceNumber()));
        }
        break;
      }
      case parameters::command_id::REPLACE_SM_RESP: {
        if(d->now_state == BOUND_TX || d->now_state == BOUND_TRX) {
          if(stopOutTransactions(header)) {
            emit parent->receive(ReplaceSmResp(header));
          } else {
            emit parent->inStatus(header, NOTFOUNDTRANS);
          }
        } else {
          emit parent->inStatus(header, NOTVALIDSTATE);
        }
        break;
      }
      case parameters::command_id::CANCEL_SM_RESP: {
        if(d->now_state == BOUND_TX || d->now_state == BOUND_TRX) {
          if(stopOutTransactions(header)) {
            emit parent->receive(CancelSmResp(header));
          } else {
            emit parent->inStatus(header, NOTFOUNDTRANS);
          }
        } else {
          emit parent->inStatus(header, NOTVALIDSTATE);
        }
        break;
      }
      case parameters::command_id::ENQUIRE_LINK: {
        if(d->now_state != CLOSED) {
          if(startInTransaction(header)) {
            emit parent->receive(EnquireLink(header));
          } else {
            emit parent->inStatus(header, TRANSFULL);
            EnquireLinkResp p_resp(parameters::command_status::ESME_RMSGQFUL,
                                   header.getSequenceNumber());
            BinaryEncoder encoder;
            encoder << p_resp;
            writeSocketData(encoder);
          }
        } else {
          emit parent->inStatus(header, NOTVALIDSTATE);
        }
        break;
      }
      case parameters::command_id::ENQUIRE_LINK_RESP: {
        if(d->now_state != CLOSED) {
          if(stopOutTransactions(header)) {
            emit parent->receive(EnquireLinkResp(header));
          } else {
            emit parent->inStatus(header, NOTFOUNDTRANS);
          }
        } else {
          emit parent->inStatus(header, NOTVALIDSTATE);
        }
        break;
      }
      case parameters::command_id::ALERT_NOTIFICATION:  {
        if(d->now_state == BOUND_RX || d->now_state == BOUND_TRX) {
          emit parent->receive(AlertNotification(header));
        } else {
          emit parent->inStatus(header, NOTVALIDSTATE);
        }
        break;
      }
      default: {
        emit parent->inStatus(header, ERRORCID);
        send(GenericNack(parameters::command_status::ESME_RINVCMDID,
                         header.getSequenceNumber()));
      }
    }
  }
}

void ClientAsyncSessionPrivate::setState(SessionStates state) {
  if(state == d->now_state) {
    return;
  }

  switch(state) {
    case CLOSED: {
      if(d->main_timer_id != -1) {
        killTimer(d->main_timer_id);
        d->main_timer_id = -1;
      }
      d->session_init_timer = 0;
      d->inactivity_timer = 0;
      d->enquire_link_timer = 0;
      d->in_speed_it = 0;
      d->now_in_speed = 0;
      d->out_speed_it = 0;
      d->now_out_speed = 0;
      clearAllQueue(NOTVALIDSTATE);
      break;
    }
    case OPEN: {
      if(d->now_state == CLOSED ||
         d->now_state == UNBOUND ||
         d->now_state == OUTBOUND) {
        d->session_init_timer = 0;
        d->inactivity_timer = 0;
        d->enquire_link_timer = 0;
        d->in_speed_it = 0;
        d->now_in_speed = 0;
        d->out_speed_it = 0;
        d->now_out_speed = 0;
        if(d->main_timer_id == -1) {
          d->main_timer_id = startTimer(1000);
        }
      }
      break;
    }
    case OUTBOUND: {
      if(d->now_state == OPEN) {
        d->session_init_timer = 0;
      }
      break;
    }
    default:
      break;
  }

  d->now_state = state;

  emit parent->stateChanged(d->now_state);

  if(d->now_state == CLOSED) {
    emit parent->closedSession();
  } else if(d->now_state == OPEN) {
    emit parent->openedSession();
  }
}

void ClientAsyncSessionPrivate::timerEvent(QTimerEvent *event) {
  if(event->timerId() == d->main_timer_id) {
    d->now_in_speed = d->in_speed_it;
    d->in_speed_it = 0;
    d->now_out_speed = d->out_speed_it;
    d->out_speed_it = 0;
    procSessionInitTimeout();
    procEnquireLinkTimeout();
    procInactivityTimeout();
    processInTransactions();
    processOutTransactions();
  }
}

void ClientAsyncSessionPrivate::sessionActivity() {
  if(d->inactivity_time > 0) {
    d->inactivity_timer = 0;
  }
}

void ClientAsyncSessionPrivate::removeAllInTransactions(StatusMessage status) {
  while(!d->in_transactions.isEmpty()) {
    ClientAsyncSessionData::Message *msg(d->in_transactions.takeFirst());
    emit parent->inStatus(msg->header, status);
    delete msg;
  }
  d->in_transactions.clear();
}

void ClientAsyncSessionPrivate::removeAllOutTransactions(StatusMessage status) {
  while(!d->out_transactions.isEmpty()) {
    ClientAsyncSessionData::Message *msg(d->out_transactions.takeFirst());
    emit parent->outStatus(msg->header, status);
    delete msg;
  }
  d->out_transactions.clear();
}

void ClientAsyncSessionPrivate::clearAllQueue(StatusMessage status) {
  removeAllInTransactions(status);
  removeAllOutTransactions(status);
}

void ClientAsyncSessionPrivate::procError(ErrorCode code,
                                          const QString &message) {
  d->error_code = code;
  d->error_message = message;

  clearAllQueue(SESSERROR);

  emit parent->error(code);

  if(d->socket->state() == QAbstractSocket::ConnectedState) {
    d->socket->abort();
  }

  setState(CLOSED);
}

void ClientAsyncSessionPrivate::procSessionInitTimeout() {
  if((d->now_state == OPEN || d->now_state == OUTBOUND) &&
     d->session_init_time > 0) {
    d->session_init_timer++;
    if(d->session_init_timer == d->session_init_time) {
      d->session_init_timer = 0;
      emit parent->sessionInitTimeout();
      closeSession();
    }
  }
}

void ClientAsyncSessionPrivate::procEnquireLinkTimeout() {
  if(d->now_state != CLOSED && d->enquire_link_time > 0) {
    d->enquire_link_timer++;
    if(d->enquire_link_timer == d->enquire_link_time) {
      d->enquire_link_timer = 0;
      emit parent->enquireLinkTimeout();
    }
  }
}

void ClientAsyncSessionPrivate::procInactivityTimeout() {
  if(d->now_state != CLOSED && d->inactivity_time > 0) {
    d->inactivity_timer++;
    if(d->inactivity_timer == d->inactivity_time) {
      d->inactivity_timer = 0;
      emit parent->inactivityTimeout();
    }
  }
}

void ClientAsyncSessionPrivate::processInTransactions() {
  if(d->transaction_time < 0)
    return;

  for(int i = 0; i < d->in_transactions.size();) {
    ClientAsyncSessionData::Message *msg(
          d->in_transactions[i]);
    msg->timer++;
    if(msg->timer >= d->transaction_time) {
      emit parent->inStatus(msg->header, TRANSTIMEOUT);
      delete msg;
      d->in_transactions.removeAt(i);
      continue;
    }
    i++;
  }
}

void ClientAsyncSessionPrivate::processOutTransactions() {
  if(d->transaction_time < 0)
    return;

  for(int i = 0; i < d->out_transactions.size();) {
    ClientAsyncSessionData::Message *msg(
          d->out_transactions[i]);
    msg->timer++;
    if(msg->timer >= d->transaction_time) {
      emit parent->outStatus(msg->header, TRANSTIMEOUT);
      delete msg;
      d->out_transactions.removeAt(i);
      continue;
    }
    i++;
  }
}

bool ClientAsyncSessionPrivate::checkInSpeed() {
  if(d->in_speed_it >= d->max_in_speed) {
    return false;
  } else {
    d->in_speed_it++;
    return true;
  }
}

bool ClientAsyncSessionPrivate::checkOutSpeed() {
  if(d->out_speed_it >= d->max_out_speed) {
    return false;
  } else {
    d->out_speed_it++;
    return true;
  }
}

bool ClientAsyncSessionPrivate::startOutTransaction(const Header &header) {
  sessionActivity();

  if(d->out_transactions.size() < d->max_out_trans) {
    ClientAsyncSessionData::Message *msg =
      new ClientAsyncSessionData::Message(header);

    d->out_transactions.push_back(msg);
    emit parent->outStatus(header, TRANSSTART);
    return true;
  } else {
    emit parent->outStatus(header, TRANSFULL);
    return false;
  }
}

bool ClientAsyncSessionPrivate::stopOutTransactions(const Header &header) {
  bool res(false);

  quint32 command_id(header.getCommandId()),
          sequence_number(header.getSequenceNumber());

  if(command_id != parameters::command_id::BIND_RECEIVER_RESP &&
     command_id != parameters::command_id::BIND_TRANSMITTER_RESP &&
     command_id != parameters::command_id::BIND_TRANSCEIVER_RESP &&
     command_id != parameters::command_id::UNBIND_RESP &&
     command_id != parameters::command_id::SUBMIT_SM_RESP &&
     command_id != parameters::command_id::SUBMIT_MULTI_RESP &&
     command_id != parameters::command_id::DATA_SM_RESP &&
     command_id != parameters::command_id::QUERY_SM_RESP &&
     command_id != parameters::command_id::REPLACE_SM_RESP &&
     command_id != parameters::command_id::CANCEL_SM_RESP &&
     command_id != parameters::command_id::ENQUIRE_LINK_RESP) {
    return res;
  }

  for(int i = 0; i < d->out_transactions.size();) {

    ClientAsyncSessionData::Message *msg(d->out_transactions[i]);

    if(msg->header.getSequenceNumber() == sequence_number) {
      bool exists(false);
      switch(msg->header.getCommandId()) {
        case parameters::command_id::BIND_RECEIVER:
          if(command_id == parameters::command_id::BIND_RECEIVER_RESP) {
            exists = true;
          }
          break;
        case parameters::command_id::BIND_TRANSMITTER:
          if(command_id == parameters::command_id::BIND_TRANSMITTER_RESP) {
            exists = true;
          }
          break;
        case parameters::command_id::BIND_TRANSCEIVER:
          if(command_id == parameters::command_id::BIND_TRANSCEIVER_RESP) {
            exists = true;
          }
          break;
        case parameters::command_id::UNBIND:
          if(command_id == parameters::command_id::UNBIND_RESP) {
            exists = true;
          }
          break;
        case parameters::command_id::SUBMIT_SM:
          if(command_id == parameters::command_id::SUBMIT_SM_RESP) {
            exists = true;
          }
          break;
        case parameters::command_id::SUBMIT_MULTI:
          if(command_id == parameters::command_id::SUBMIT_MULTI_RESP) {
            exists = true;
          }
          break;
        case parameters::command_id::DATA_SM:
          if(command_id == parameters::command_id::DATA_SM_RESP) {
            exists = true;
          }
          break;
        case parameters::command_id::QUERY_SM:
          if(command_id == parameters::command_id::QUERY_SM_RESP) {
            exists = true;
          }
          break;
        case parameters::command_id::REPLACE_SM:
          if(command_id == parameters::command_id::REPLACE_SM_RESP) {
            exists = true;
          }
          break;
        case parameters::command_id::CANCEL_SM:
          if(command_id == parameters::command_id::CANCEL_SM_RESP) {
            exists = true;
          }
          break;
        case parameters::command_id::ENQUIRE_LINK:
          if(command_id == parameters::command_id::ENQUIRE_LINK_RESP) {
            exists = true;
          }
          break;
      }

      if(exists) {
        emit parent->outStatus(msg->header, RESPONSED);
        res = exists;
        delete msg;
        d->out_transactions.removeAt(i);
        continue;
      }
    }
    i++;
  }

  return res;
}

void ClientAsyncSessionPrivate::deleteOutTransaction(const Header &header) {
  for(int i = 0; i < d->out_transactions.size(); i++) {
    ClientAsyncSessionData::Message *msg(d->out_transactions[i]);

    if(msg->header == header) {
      delete msg;
      d->out_transactions.removeAt(i);
      break;
    }
  }
}

bool ClientAsyncSessionPrivate::startInTransaction(const Header &header) {
  sessionActivity();

  if(d->in_transactions.size() < d->max_in_trans) {
    ClientAsyncSessionData::Message *msg =
      new ClientAsyncSessionData::Message(header);

    d->in_transactions.push_back(msg);
    emit parent->inStatus(header, TRANSSTART);
    return true;
  } else {
    emit parent->inStatus(header, TRANSFULL);
    return false;
  }
}

bool ClientAsyncSessionPrivate::stopInTransactions(const Header &header) {
  bool res(false);

  quint32 command_id(header.getCommandId()),
          sequence_number(header.getSequenceNumber());

  if(command_id != parameters::command_id::DELIVER_SM_RESP &&
     command_id != parameters::command_id::DATA_SM_RESP &&
     command_id != parameters::command_id::ENQUIRE_LINK_RESP) {
    return res;
  }

  for(int i = 0; i < d->in_transactions.size();) {

    ClientAsyncSessionData::Message *msg(d->in_transactions[i]);

    if(msg->header.getSequenceNumber() == sequence_number) {
      bool exists(false);
      switch(msg->header.getCommandId()) {
        case parameters::command_id::DELIVER_SM:
          if(command_id == parameters::command_id::DELIVER_SM_RESP) {
            exists = true;
          }
          break;
        case parameters::command_id::DATA_SM:
          if(command_id == parameters::command_id::DATA_SM_RESP) {
            exists = true;
          }
          break;
        case parameters::command_id::ENQUIRE_LINK:
          if(command_id == parameters::command_id::ENQUIRE_LINK_RESP) {
            exists = true;
          }
          break;
      }

      if(exists) {
        emit parent->inStatus(msg->header, RESPONSED);
        res = exists;
        delete msg;
        d->in_transactions.removeAt(i);
        continue;
      }
    }
    i++;
  }

  return res;
}

void ClientAsyncSessionPrivate::deleteInTransaction(const Header &header) {
  for(int i = 0; i < d->in_transactions.size(); i++) {
    ClientAsyncSessionData::Message *msg(d->in_transactions[i]);

    if(msg->header == header) {
      delete msg;
      d->in_transactions.removeAt(i);
      break;
    }
  }
}

bool ClientAsyncSessionPrivate::writeSocketData(const QByteArray &bytes) {
  if(d->socket->state() == QAbstractSocket::ConnectedState &&
     d->socket->isValid()) {

    qint64 write_bytes(d->socket->write(bytes));
    d->socket->flush();

    if(write_bytes < bytes.size()) {
      return d->socket->waitForBytesWritten(
        ClientAsyncSessionData::sock_oper_timeout);
    } else {
      return true;
    }
  } else {
    return false;
  }
}

ClientAsyncSession::ClientAsyncSession(
  int max_in_speed, // msec
  int max_out_speed, // msec
  int session_init_time,
  int enquire_link_time,
  int inactivity_time,
  int transaction_time,
  int max_in_trans,
  int max_out_trans,
  bool threaded) :
  p(new ClientAsyncSessionPrivate(this,
                                  max_in_speed,
                                  max_out_speed,
                                  session_init_time,
                                  enquire_link_time,
                                  inactivity_time,
                                  transaction_time,
                                  max_in_trans,
                                  max_out_trans,
                                  threaded)) {
  if(threaded) {
    QTimer::singleShot(0, this, SLOT(startProcess()));
  }
}

ClientAsyncSession::~ClientAsyncSession() {
  if(p) {
    if(p->d->thread) {
      if(p->d->thread->isRunning()) {
        p->d->thread->quit();
        p->d->thread->wait();
      }
    }
    delete p;
    p = 0;
  }
}

void ClientAsyncSession::startProcess() {
  if(p->d->thread && !p->d->thread->isRunning()) {
    p->d->thread->start();
  }
}

void ClientAsyncSession::setMaxInSpeed(int max_in_speed) {
  p->d->max_in_speed = max_in_speed;
}

void ClientAsyncSession::setMaxOutSpeed(int max_out_speed) {
  p->d->max_out_speed = max_out_speed;
}

void ClientAsyncSession::setSessionInitTime(int session_init_time) {
  p->d->session_init_time = session_init_time;
}

void ClientAsyncSession::setEnquireLinkTime(int enquire_link_time) {
  p->d->enquire_link_time = enquire_link_time;
}

void ClientAsyncSession::setInactivityTime(int inactivity_time) {
  p->d->inactivity_time = inactivity_time;
}

void ClientAsyncSession::setTransactionTime(int transaction_time) {
  p->d->transaction_time = transaction_time;
}

void ClientAsyncSession::setMaxInTrans(int max_in_trans) {
  p->d->max_in_trans = max_in_trans;
}

void ClientAsyncSession::setMaxOutTrans(int max_out_trans) {
  p->d->max_out_trans = max_out_trans;
}

smpp::sessions::SessionStates ClientAsyncSession::state() const {
  return p->d->now_state;
}

QHostAddress ClientAsyncSession::peerAddress() const {
  return p->d->peer_address;
}

quint16 ClientAsyncSession::peerPort() const {
  return p->d->peer_port;
}

QHostAddress ClientAsyncSession::localAddress() const {
  return p->d->local_address;
}

quint16 ClientAsyncSession::localPort() const {
  return p->d->local_port;
}

int ClientAsyncSession::getMaxInSpeed() const {
  return p->d->max_in_speed;
}

int ClientAsyncSession::getMaxOutSpeed() const {
  return p->d->max_out_speed;
}

int ClientAsyncSession::getNowInSpeed() const {
  return p->d->now_in_speed;
}

int ClientAsyncSession::getNowOutSpeed() const {
  return p->d->now_out_speed;
}

int ClientAsyncSession::getSessionInitTime() const {
  return p->d->session_init_time;
}

int ClientAsyncSession::getEnquireLinkTime() const {
  return p->d->enquire_link_time;
}

int ClientAsyncSession::getInactivityTime() const {
  return p->d->inactivity_time;
}

int ClientAsyncSession::getTransactionTime() const {
  return p->d->transaction_time;
}

int ClientAsyncSession::getMaxInTrans() const {
  return p->d->max_in_trans;
}

int ClientAsyncSession::getMaxOutTrans() const {
  return p->d->max_out_trans;
}

int ClientAsyncSession::getOpenedInTrans() const {
  return p->d->in_transactions.size();
}

int ClientAsyncSession::getOpenedOutTrans() const {
  return p->d->out_transactions.size();
}

smpp::sessions::ErrorCode ClientAsyncSession::error() const {
  return p->d->error_code;
}

QString ClientAsyncSession::errorString() const {
  return p->d->error_message;
}

void ClientAsyncSession::openSession(const QHostAddress &host_name,
                                     quint16 port) {
  emit p->sigOpenSession(host_name, port);
}

void ClientAsyncSession::closeSession() {
  emit p->sigCloseSession();
}

void ClientAsyncSession::send(const BindTransmitter &packet) {
  emit p->sigSend(packet);
}

void ClientAsyncSession::send(const BindReceiver &packet) {
  emit p->sigSend(packet);
}

void ClientAsyncSession::send(const BindTransceiver &packet) {
  emit p->sigSend(packet);
}

void ClientAsyncSession::send(const Unbind &packet) {
  emit p->sigSend(packet);
}

void ClientAsyncSession::send(const GenericNack &packet) {
  emit p->sigSend(packet);
}

void ClientAsyncSession::send(const SubmitSm &packet) {
  emit p->sigSend(packet);
}

void ClientAsyncSession::send(const SubmitMulti &packet) {
  emit p->sigSend(packet);
}

void ClientAsyncSession::send(const DeliverSmResp &packet) {
  emit p->sigSend(packet);
}

void ClientAsyncSession::send(const DataSm &packet) {
  emit p->sigSend(packet);
}

void ClientAsyncSession::send(const DataSmResp &packet) {
  emit p->sigSend(packet);
}

void ClientAsyncSession::send(const QuerySm &packet) {
  emit p->sigSend(packet);
}

void ClientAsyncSession::send(const ReplaceSm &packet) {
  emit p->sigSend(packet);
}

void ClientAsyncSession::send(const CancelSm &packet) {
  emit p->sigSend(packet);
}

void ClientAsyncSession::send(const EnquireLink &packet) {
  emit p->sigSend(packet);
}

void ClientAsyncSession::send(const EnquireLinkResp &packet) {
  emit p->sigSend(packet);
}

} // namespace sessions

} // namespace smpp
